﻿using Newtonsoft.Json;
using System;
using System.Text;

namespace JCCY
{
    /// <summary>
    /// 配方
    /// </summary>
    public class GameRecipe
    {
        [JsonProperty("产物")]
        public string 产物 { get; private set; }
        [JsonProperty("生产时间")]
        public float 生产时间 { get; private set; }
        [JsonProperty("产出数量")]
        public float 产出数量 { get; private set; }
        [JsonProperty("产出建筑")]
        public string 产出建筑 { get; private set; }
        [JsonProperty("原料组")]
        public List<Ingredient> 原料组 { get; private set; } = new List<Ingredient>();
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="product">产物</param>
        /// <param name="OutPutJZ">生产时间</param>
        /// <param name="outputCount">生产时间</param>
        /// <param name="productionTime">产出数量</param>

        public GameRecipe(string product, string OutPutJZ, float outputCount, float productionTime, params Ingredient[] ingredients)
        {
            this.产物 = product;
            this.生产时间 = productionTime;
            this.产出数量 = outputCount;
            this.产出建筑 = OutPutJZ;
            this.原料组 = new List<Ingredient>(ingredients);
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="product">产物</param>
        /// <param name="OutPutJZ">生产时间</param>
        /// <param name="outputCount">生产时间</param>
        /// <param name="productionTime">产出数量</param>

        public GameRecipe(string product, string OutPutJZ, float outputCount, float productionTime)
        {
            this.产物 = product;
            this.生产时间 = productionTime;
            this.产出数量 = outputCount;
            this.产出建筑 = OutPutJZ;
        }
        /// <summary>
        /// 转换为60秒配方
        /// </summary>
        /// <returns></returns>
        public GameRecipe To60SecRecipe()
        {

            var bei = 60 / this.生产时间;

            GameRecipe recipe = new GameRecipe(this.产物, this.产出建筑, this.产出数量 * bei, 60f);
            if (this.原料组.Count > 0)
            {
                for (int i = 0; i < 原料组.Count; i++)
                {
                    recipe.原料组.Add(new Ingredient(原料组[i].名称, 原料组[i].数量 * bei));

                }

            }
            return recipe;

        }

        private GameRecipe() { }
    }
    /// <summary>
    /// 原材料
    /// </summary>
    public class Ingredient
    {
        [JsonProperty("名称")]
        public string 名称 { get; private set; } = "";
        [JsonProperty("数量")]
        public float 数量 { get; set; } = 0f;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">名称</param>
        /// <param name="count">数量</param>
        public Ingredient(string name, float count)
        {
            this.名称 = name;
            this.数量 = count;
        }

    }

    /// <summary>
    /// 配方组
    /// </summary>
    public class GameRecipeGroup
    {
        [JsonProperty("版本号")]
        public string 版本号 { get; set; } = "";
        /// <summary>
        /// 配方列表
        /// </summary>
        public List<GameRecipe> Recipes { get; private set; } = new List<GameRecipe>();
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">名称</param>
        public GameRecipeGroup()
        {

        }
        /// <summary>
        /// 添加配方
        /// </summary>
        /// <param name="recipe"></param>
        public void AddRecipe(GameRecipe recipe)
        {
            this.Recipes.Add(recipe);
        }
        /// <summary>
        /// 添加一组配方
        /// </summary>
        /// <param name="recipe"></param>
        public void AddRecipe(List<GameRecipe> recipes)
        {
            this.Recipes.AddRange(recipes);
        }


        /// <summary>
        /// 根据名字获取配方 获取失败返回NULL
        /// </summary>
        /// <param name="name">配方名</param>
        /// <returns></returns>
        public GameRecipe GetGameRecipeByName(string name)
        {
            return Recipes.FirstOrDefault(x => x.产物 == name);
        }
    }


    /// <summary>
    /// 序列化工具
    /// </summary>
    public static class SerializeConvert
    {
        #region 序列化 Jso序列化和反序列化

        /// <summary>
        /// 将一个类序列化为Json字符串
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static string ToJsonString(this object item, bool isFormat = false)
        {
            if (isFormat)
            {
                return Newtonsoft.Json.JsonConvert.SerializeObject(item, Newtonsoft.Json.Formatting.Indented);
            }
            return Newtonsoft.Json.JsonConvert.SerializeObject(item);
        }
        /// <summary>
        /// 反序列化Json字符串为类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="json"></param>
        /// <returns></returns>
        public static T? FromJsonString<T>(this string json)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json);
        }
        /// <summary>
        /// 反序列化Json字符串为类
        /// </summary>
        /// <param name="json"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object? FromJsonString(this string json, Type type)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject(json, type);
        }
        /// <summary>
        /// Json序列化为数据对象
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static byte[] JsonSerializeToBytes(this object item, bool isFormat = false)
        {
            if (isFormat)
            {
                return Encoding.UTF8.GetBytes(ToJsonString(item, true));
            }
            return Encoding.UTF8.GetBytes(ToJsonString(item));
        }
        /// <summary>
        /// Json序列化至文件
        /// </summary>
        /// <param name="item"></param>
        /// <param name="path"></param>
        public static void JsonSerializeToFile(this object item, string path, bool isFormat = true)
        {

            System.IO.File.WriteAllBytes(path, JsonSerializeToBytes(item, isFormat));
        }
        /// <summary>
        /// 将文件反序列化成类
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="path"></param>
        /// <returns></returns>
        public static T? JsonDeserializeFromFile<T>(string path)
        {
            return FromJsonString<T>(System.IO.File.ReadAllText(path));
        }

        #endregion


    }

    /// <summary>
    /// 配方计算器
    /// </summary>
    public class GameRecipeCalculator
    {
        private GameRecipeGroup gameRecipeGroup;
        /// <summary>
        /// 构造函数
        /// </summary>
        public GameRecipeCalculator(string json)
        {
          

            var re = json.FromJsonString<GameRecipeGroup>();

            if (re != null)
            {
                this.gameRecipeGroup = re;
            }
            else
            {
                this.gameRecipeGroup = new GameRecipeGroup();
            }
        }
        /// <summary>
        /// 获取配方名列表
        /// </summary>
        /// <returns></returns>
        public List<string> GetNameList()
        {

            List<string> list = new List<string>();

            foreach (var item in gameRecipeGroup.Recipes)
            {
                if (item.产物 != "")
                {
                    list.Add(item.产物);
                }

            }
            return list;
        }
        /// <summary>
        /// 计算配方成本
        /// </summary>
        /// <param name="name">名字</param>
        /// <param name="count">需求数量</param>
        public bool CalculateTotalCost(string name, float count, out string msg, out NeedsCatalogList needsCatalogsList)
        {
            msg = "";
            var Recipe = gameRecipeGroup.GetGameRecipeByName(name);
            needsCatalogsList = new NeedsCatalogList();
            if (Recipe == null)
            {
                msg = $"找不到配方:{name}";

                return false;
            }
            var Repice60 = Recipe.To60SecRecipe();

            needsCatalogsList.Add(Repice60, count);

            foreach (var item in Repice60.原料组)
            {
                try
                {
                    CalculateTotalCost2(item.名称, item.数量 * count, ref needsCatalogsList);
                }
                catch (Exception ex)
                {

                    msg = ex.Message;
                    return false;
                }


            }


            msg = "成功";
            return true;
        }
        /// <summary>
        /// 递归计算
        /// </summary>
        /// <param name="name"></param>
        /// <param name="NeedCount"></param>
        /// <param name="msg"></param>
        /// <param name="needsCatalogs"></param>
        /// <returns></returns>
        private void CalculateTotalCost2(string name, float NeedCount, ref NeedsCatalogList needsCatalogsList)
        {



            var Recipe = gameRecipeGroup.GetGameRecipeByName(name);
            if (Recipe == null)
            {
                throw new Exception($"找不到配方:{name}");
            }
            var Repice60 = Recipe.To60SecRecipe();
            if (Repice60.原料组.Count == 0)
            {
                needsCatalogsList.Add(Repice60, NeedCount);
                return;
            }
            var bei = NeedCount / Repice60.产出数量;


            needsCatalogsList.Add(Repice60, bei);
            foreach (var item in Repice60.原料组)
            {
                CalculateTotalCost2(item.名称, item.数量, ref needsCatalogsList);
            }

        }

    }















    /// <summary>
    /// 需求列表 用于承载结果
    /// </summary>
    public class NeedsCatalog
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; private set; } = "";
        /// <summary>
        /// 每分钟输出量文本
        /// </summary>
        public string OutputPerMin { get { return $"{OutputPerMinFloat.ToString("f1")}/Min"; } }

        /// <summary>
        /// 每分钟输出量
        /// </summary>
        public float OutputPerMinFloat
        {
            get; set;
        }
        /// <summary>
        /// 生产设备
        /// </summary>
        public string ProductionEquipment { get { return $"{ProductionEquipmentString}*{ProductionEquipmentCount.ToString("f1")}"; } }

        /// <summary>
        /// 生产设备文本
        /// </summary>
        public string ProductionEquipmentString
        {
            get; set;
        }
        /// <summary>
        /// 生产设备数量
        /// </summary>
        public float ProductionEquipmentCount { get; set; }



        /// <summary>
        /// 配方表
        /// </summary>
        public string Recipe { get; private set; } = "";
        /// <summary>
        /// 使用配方构造此函数
        /// </summary>
        /// <param name="gameRecipe">配方</param>
        /// <param name="Count">数量</param>
        public NeedsCatalog(string name, float OutputPerMinFloat, string ProductionEquipmentString, float ProductionEquipmentCount, string Recipe)
        {
            this.Name = name;
            this.OutputPerMinFloat = OutputPerMinFloat;
            this.ProductionEquipmentString = ProductionEquipmentString;
            this.ProductionEquipmentCount = ProductionEquipmentCount;
            this.Recipe = Recipe;
        }




        public override string ToString()
        {
            return $"名称:{this.Name} 每分钟产出量:{this.OutputPerMin} 生产设备:{this.ProductionEquipment} 配方表:{this.Recipe}";
        }
    }


    public class NeedsCatalogList
    {
        public List<NeedsCatalog> NeedsCatalogs { get; private set; } = new List<NeedsCatalog>();
        /// <summary>
        /// 添加需求
        /// </summary>
        /// <param name="name">配方</param>
        /// <param name="Count">数量</param>
        public void Add(GameRecipe gameRecipe, float Count)
        {
            foreach (var item in NeedsCatalogs)
            {
                if (item.Name == gameRecipe.产物)
                {
                    AddOld(gameRecipe, Count);
                    return;
                }




            }

            AddNew(gameRecipe, Count);

        }
        private void AddNew(GameRecipe gameRecipe, float Count)
        {
            var name = gameRecipe.产物;

            var OutputPerMinFloat = 0f;
            var ProductionEquipmentString = gameRecipe.产出建筑;
            var Recipe = "";
            var ProductionEquipmentCount = 0f;
            if (gameRecipe.原料组.Count <= 0)
            {
                OutputPerMinFloat = Count;

                Recipe = "";
            }
            else
            {
                OutputPerMinFloat = gameRecipe.产出数量 * Count * (60 / gameRecipe.生产时间);


                ProductionEquipmentCount = Count;
                foreach (var item in gameRecipe.原料组)
                {
                    if (Recipe == "")
                    {
                        Recipe += $"{item.名称}*{item.数量.ToString("f1")}";
                    }
                    else
                    {
                        Recipe += $"+{item.名称}*{item.数量.ToString("f1")}";
                    }

                }
                Recipe += $"=>{name}*{gameRecipe.产出数量.ToString("f1")}/Min";
            }

            this.NeedsCatalogs.Add(new NeedsCatalog(name, OutputPerMinFloat, ProductionEquipmentString, Count, Recipe));
        }

        private void AddOld(GameRecipe gameRecipe, float Count)
        {
            for (int i = 0; i < NeedsCatalogs.Count; i++)
            {
                if (NeedsCatalogs[i].Name == gameRecipe.产物)
                {

                    if (gameRecipe.原料组.Count <= 0)
                    {
                        NeedsCatalogs[i].OutputPerMinFloat += Count;
                    }
                    else
                    {

                        NeedsCatalogs[i].OutputPerMinFloat += gameRecipe.产出数量 * Count * (60 / gameRecipe.生产时间);

                        NeedsCatalogs[i].ProductionEquipmentCount += Count;

                    }

                    return;

                }

            }







        }
    }





}
